package com.bestcem.xm.label.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.label.constant.Constants;
import com.bestcem.xm.label.dao.LabelDao;
import com.bestcem.xm.label.dao.LabelGroupDao;
import com.bestcem.xm.label.entity.pojo.LabelDO;
import com.bestcem.xm.label.entity.pojo.LabelGroupDO;
import com.bestcem.xm.label.entity.pojo.LabelValueDO;
import com.bestcem.xm.label.enums.MemberTypeEnum;
import com.bestcem.xm.label.grpc.service.param.label.*;
import com.bestcem.xm.label.service.LabelGroupService;
import com.bestcem.xm.label.service.LabelMemberService;
import com.bestcem.xm.label.service.LabelService;
import com.bestcem.xm.label.service.LabelValueService;
import com.bestcem.xm.label.service.dto.label.LabelDTO;
import com.bestcem.xm.label.service.dto.label.LabelInfoDTO;
import com.bestcem.xm.member.dao.ExtendFieldDao;
import com.bestcem.xm.member.entity.mysql.MemberEntity;
import com.bestcem.xm.member.service.GroupService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.COMPUTES_INFO;
import static com.bestcem.xm.member.constant.Constants.INDIV_INFO;

/**
 * 标签服务
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/5/21
 */

@Slf4j
@Service
public class LabelServiceImpl implements LabelService {

    /**
     * 标签数据访问
     */
    @Resource
    private LabelDao labelDao;

    @Resource
    private LabelGroupDao labelGroupDao;

    /**
     * 标签值相关服务
     */
    @Resource
    private LabelValueService labelValueService;

    /**
     * 标签组相关服务
     */
    @Resource
    private LabelGroupService labelGroupService;

    /**
     * 自动打标签服务
     */
    @Resource
    private LabelMemberService labelMemberService;

    /**
     * 扩展字段dao
     */
    @Resource
    private ExtendFieldDao extendFieldDao;

    @Resource
    private GroupService groupService;

    /**
     * 联系人 客户端
     */
    //@Autowired
    //private XmMemberClient xmMemberClient;
    @Override
    public ServiceResult<List<LabelDTO>> listGroupLabels(ListGroupLabelsParam param) {
        List<String> labelGroupIds = param.getLabelGroupIds();
        List<LabelDTO> labelDTOS = labelDao.listByLabelGroupIds(labelGroupIds);
        return ServiceResult.success(labelDTOS);
    }

    @Override
    public ServiceResult<List<LabelDTO>> getLabelInfo(List<String> labelIds) {
        List<LabelDTO> labels = labelDao.listByIds(labelIds);
        return ServiceResult.success(labels);
    }

    @Override
    public ServiceResult<String> createLabel(CreateLabelParam param) {

        // 个人联系人才判断
        // 组织下 标签不能重名
        if (MemberTypeEnum.MEMBER_MTYPE_INDIVIDUAL.getIndex().equals(param.getAttribute())) {
            Boolean isValid = checkFieldNameLabelNameValid(param.getName(), param.getOrgId(), param.getAttribute());
            if (!isValid) {
                return ServiceResult.fail("已存在同名标签或字段，请修改后重试");
            }
        }

        List<CreateLabelValueParam> values = param.getValues();

        // 循环 对 标签值的condition进行校验
        for (int i = 0; i < values.size(); i++) {
            CreateLabelValueParam value = values.get(i);
            String message = checkValueCondition(param.getAuto(), value.getCondition(), param.getOrgId());
            if (StrUtil.isNotEmpty(message)) {
                return ServiceResult.fail(message);
            }
        }

        // 判断标签组是否存在
        LabelGroupDO labelGroup = labelGroupDao.getById(param.getLabelGroupId());
        if (ObjectUtil.isEmpty(labelGroup)) {
            return ServiceResult.fail("标签组不存在");
        }

        List<CreateLabelValueParam> labelValueParam = param.getValues();
        // 判断创建的标签值是否重名
        Set<String> createLabelValueSet =
                labelValueParam.stream().map(CreateLabelValueParam::getValue).collect(Collectors.toSet());
        if (createLabelValueSet.size() != labelValueParam.size()) {
            return ServiceResult.fail("新增的标签值名称重复");
        }

        LabelDO label = CreateLabelParam.convert(param);
        label.setLabelGroupId(param.getLabelGroupId());
        label.setCreateTime(DateUtil.getCommonDate());
        label.setUpdateTime(label.getCreateTime());

        // 创建标签
        String labelId = labelDao.save(label);
        // 批量新建标签值
        labelValueService.save(labelValueParam, param, labelId, labelGroup.getName());
        return ServiceResult.success(labelId);
    }

    /**
     * 判断标签名称是否重复
     *
     * @param name
     * @param orgId
     * @param attribute
     * @return
     */
    private Boolean checkFieldNameLabelNameValid(String name, String orgId, Integer attribute) {
        // 检查字段是否与基本信息相同
        List<String> infos =
                INDIV_INFO.stream().map(info -> info.getDisplayName()).collect(Collectors.toList());
        if (infos.contains(name)) {
            if (log.isInfoEnabled()) {
                log.info("该名称 {} 与基础字段 {} 冲突", name, infos);
            }
            return false;
        }
        // 检查字段是否与扩展字段相同
        // List<ExtendFieldDO> extendFields = extendFieldDao.findByNames(orgId,attribute,Collections.singletonList(name));
        Integer count = extendFieldDao.countByOrgIdAndMemberTypeAndName(orgId, attribute, name);
        if (count > 0) {
            if (log.isInfoEnabled()) {
                log.info("字段名重复：{}", name);
            }
            return false;
        }
        // 检查字段是否与标签名相同(个人联系人需要检查)
        count = labelDao.countByNameAndOrgId(name, orgId);
        if (count > 0) {
            if (log.isInfoEnabled()) {
                log.info("该名称 {} 与该组织下 {} 其他标签名称冲突", name, orgId);
            }
            return false;
        }
        // 检查字段是否与数据字段名相同
        List<String> computesInfos =
                COMPUTES_INFO.stream().map(info -> info.getDisplayName()).collect(Collectors.toList());
        if (computesInfos.contains(name)) {
            if (log.isInfoEnabled()) {
                log.info("该名称 {} 与计算字段 {} 冲突", name, computesInfos);
            }
            return false;
        }
        return true;
    }

    @Override
    public ServiceResult<String> updateLabel(UpdateLabelParam param) {
        // 根据id 获取标签
        LabelDO label = labelDao.getById(param.getId());
        if (ObjectUtil.isEmpty(label)) {
            // 标签不存在
            return ServiceResult.fail("标签不存在");
        }

        // 判断标签名称是否重复
        if (MemberTypeEnum.MEMBER_MTYPE_INDIVIDUAL.getIndex().equals(param.getAttribute()) && !param.getName().equals(label.getName())) {
            // 判断标签名是否重复
            Boolean isValid = checkFieldNameLabelNameValid(param.getName(), param.getOrgId(), param.getAttribute());
            if (!isValid) {
                return ServiceResult.fail("已存在同名标签或字段，请修改后重试");
            }
        }

        // 获取标签组信息
        LabelGroupDO labelGroup = labelGroupDao.getById(param.getLabelGroupId());
        if (ObjectUtil.isEmpty(labelGroup)) {
            return ServiceResult.fail("标签组不存在");
        }

        // 从db中获取当前标签标签值的数量
        // Integer total = labelValueService.countByLabelId(param.getId());
        Integer total = labelValueService.countByOrgIdAndLabelId(labelGroup.getOrgId(), param.getId());
        // 需要删除的标签值
        List<String> deleteValues = CollectionUtil.isNotEmpty(param.getDeleteValues()) ? param.getDeleteValues() : new ArrayList<>();
        // 需要添加的标签值
        List<CreateLabelValueParam> insertValues = CollectionUtil.isNotEmpty(param.getInsertValues()) ? param.getInsertValues() : new ArrayList<>();
        // 需要更新的标签值
        List<UpdateLabelValueParam> updateValues = CollectionUtil.isNotEmpty(param.getUpdateValues()) ? param.getUpdateValues() : new ArrayList<>();

        // 判断当前标签标签值的数量是否大于30
        if ((total + insertValues.size() - deleteValues.size()) > Constants.LABEL_VALUE_MAX) {
            return ServiceResult.fail("标签值最多30个");
        }

        // 对标签值名称的重复性判断
        ServiceResult<String> result = checkValue(deleteValues, insertValues, updateValues, param.getId());
        if (!result.isSuccess()) {
            return ServiceResult.fail(result.getMsg());
        }
        // 判断
        for (int i = 0; i < insertValues.size(); i++) {
            CreateLabelValueParam value = insertValues.get(i);
            // 校验标签值参数
            String message = checkValueCondition(param.getAuto(), value.getCondition(), param.getOrgId());
            if (StrUtil.isNotEmpty(message)) {
                return ServiceResult.fail(message);
            }
        }

        for (int i = 0; i < updateValues.size(); i++) {
            UpdateLabelValueParam value = updateValues.get(i);
            // 校验标签值参数
            String message = checkValueCondition(param.getAuto(), value.getCondition(), param.getOrgId());
            if (StrUtil.isNotEmpty(message)) {
                return ServiceResult.fail(message);
            }
        }

        if (CollectionUtil.isNotEmpty(deleteValues)) {
            // 删除联系人标签以及标签值
            if (!deleteLabelValue(param.getAttribute(), param.getOrgId(), deleteValues)) {
                return ServiceResult.fail("标签更新失败");
            }
        }

        // 修改标签信息
        LabelDO labelDO = new LabelDO();
        labelDO.setLabelId(param.getId());
        labelDO.setName(param.getName());
        labelDO.setLabelGroupId(param.getLabelGroupId());
        labelDO.setManual(param.getManual() ? 1 : 0);
        labelDO.setAuto(param.getAuto() ? 1 : 0);
        labelDO.setUpdateTime(DateUtil.getCommonDate());
        Boolean updateResult = labelDao.updateLabel(labelDO);
        if (!updateResult) {
            log.error("标签更新失败 {}", param.toString());
            return ServiceResult.fail("标签更新失败");
        }

        // 修改标签值信息（冗余的标签/标签组信息）
        Boolean updateTagValueResult = labelValueService.update(param, labelGroup.getName(), label.getName());
        if (!updateTagValueResult) {
            log.error("标签值更新失败 {}", param.toString());
            return ServiceResult.fail("标签更新失败");
        }

        // 添加标签值
        if (CollectionUtil.isNotEmpty(insertValues)) {
            labelValueService.save(insertValues, param, param.getId(), labelGroup.getName());
        }
        // 批量修改标签值
        if (CollectionUtil.isNotEmpty(updateValues)) {
            List<LabelValueDO> labelValueDOS = updateValues.stream().map(value -> {
                LabelValueDO labelValueDO = new LabelValueDO();
                labelValueDO.setLabelValueId(value.getId());
                labelValueDO.setSeq(value.getSerial());
                labelValueDO.setValue(value.getValue());
                labelValueDO.setCondition(value.getCondition());
                labelValueDO.setUpdateTime(DateUtil.getCommonDate());
                return labelValueDO;
            }).collect(Collectors.toList());
            Boolean bulkWriteResult = labelValueService.batchUpdate(labelValueDOS);
            if (!bulkWriteResult) {
                return ServiceResult.fail("标签更新失败");
            }
        }

        // 异步更新联系人分组信息
        groupService.sendGroupMessages(labelGroup.getOrgId());

        return ServiceResult.success(param.getId());
    }

    /**
     * 检查标签值名称是否重复
     *
     * @param deleteValues
     * @param insertValues
     * @param updateValues
     * @param id
     * @return
     */
    private ServiceResult<String> checkValue(List<String> deleteValues, List<CreateLabelValueParam> insertValues,
                                             List<UpdateLabelValueParam> updateValues, String id) {

        // 获取该标签下所有的标签值名称
        List<LabelValueDO> labelValues = labelValueService.findValueByLabelId(id);
        // 数据库标签值根据标签值id分组
        Map<String, List<LabelValueDO>> dbValueMap =
                labelValues.stream().collect(Collectors.groupingBy(LabelValueDO::getLabelValueId));
        // 移除将要删除的标签值，将要删除的标签值不参与标签值名称重复校验
        if (!CollectionUtil.isEmpty(deleteValues)) {
            deleteValues.forEach(valueId -> {
                dbValueMap.remove(valueId);
            });
        }

        // 数据库中存在的标签值名称（除去将要删除的）
        Set<String> dbValues = new HashSet<>();

        // 若 没有全部删除 标签值
        if (CollectionUtil.isNotEmpty(dbValueMap)) {
            // 获取数据库中已经存的标签value
            dbValueMap.forEach((k, v) -> {
                dbValues.add(v.get(0).getValue());
            });

        }

        // 新的标签值名称（添加的和更新的）
        Set<String> newValueSet = new HashSet<>();
        // 对新增的标签值进行名称校验
        if (!CollectionUtil.isEmpty(insertValues)) {
            List<String> insertValueList =
                    insertValues.stream().map(CreateLabelValueParam::getValue).collect(Collectors.toList());
            newValueSet.addAll(insertValueList);
            // 获取交集
            Collection<String> intersection = CollUtil.intersection(dbValues, newValueSet);
            if (CollUtil.isNotEmpty(intersection)) {
                return ServiceResult.fail("新增或编辑的标签值重复");
            }
        }
        if (!CollectionUtil.isEmpty(updateValues)) {
            for (int i = 0; i < updateValues.size(); i++) {
                UpdateLabelValueParam updateValue = updateValues.get(i);
                if (dbValueMap.containsKey(updateValue.getId())) {
                    LabelValueDO labelValue = dbValueMap.get(updateValue.getId()).get(0);
                    // 标签值名称修改了
                    if (!updateValue.getValue().equals(labelValue.getValue())) {
                        if (dbValues.contains(updateValue.getValue())) {
                            return ServiceResult.fail("新增或编辑的标签值重复");
                        }
                    }
                }
                newValueSet.add(updateValue.getValue());
            }
        }

        if (newValueSet.size() != (insertValues.size() + updateValues.size())) {
            return ServiceResult.fail("新增或编辑的标签值重复");
        }
        return ServiceResult.success();
    }

    /**
     * 校验 标签值condition
     * auto = true condition 必须存在
     * auto = false condition 必须不存在
     *
     * @param auto
     * @param condition
     * @param orgId
     * @return
     */
    private String checkValueCondition(Boolean auto, String condition, String orgId) {
        if (auto && StrUtil.isEmpty(condition)) {
            return "自动打标签的标签值条件不存在";
        } else if (!auto && StrUtil.isNotEmpty(condition)) {
            return "非自动打标签存在条件";
        } else if (auto) {
            // 解析标签值
            ServiceResult<Wrapper<MemberEntity>> result = labelMemberService.compileCondition(orgId, condition);
            if (!result.isSuccess()) {
                return result.getMsg();
            }
        }
        return null;
    }

    @Override
    public ServiceResult<String> deleteLabel(DeleteLabelParam param) {

        // 根据标签id获取所有的标签值
//        List<LabelValueDO> labelValues = labelValueService.findByLabelId(param.getId());
//        if (CollectionUtil.isNotEmpty(labelValues)) {
//            // 获取所有的标签值id
//            List<String> labelValueIdList =
//                    labelValues.stream().map(LabelValueDO::getLabelValueId).collect(Collectors.toList());
//            // 获取联系人类型 因为同一个标签在标签值的联系人类型是一致的
//            Integer attribute = labelValues.get(0).getMemberType();
//
//            // 删除联系人标签以及标签值
//            if (!deleteLabelValue(attribute, param.getOrgId(), labelValueIdList)) {
//                return ServiceResult.fail("标签删除失败");
//            }
//        }
        // 删除该组织标签下的标签值
        labelValueService.deleteByOrgIdAndLabelId(param.getOrgId(), param.getId());

        // 删除标签
        Boolean deleteResult = labelDao.delete(param.getId());
        if (!deleteResult) {
            log.error("标签删除失败，标签id {}", param.getId());
            return ServiceResult.fail("标签删除失败");
        }

        // 异步更新联系人分组信息
        groupService.sendGroupMessages(param.getOrgId());

        return ServiceResult.success(param.getId());
    }

    @Override
    public ServiceResult<List<LabelDTO>> deleteByLabelGroupId(String labelGroupId) {

        List<LabelDO> labels = labelDao.deleteAndListByLabelGroupId(null, labelGroupId);

        List<LabelDTO> labelDTOS = labels.stream().map(this::convertToLabelDTO).collect(Collectors.toList());

        // 异步更新联系人分组信息
        if (!CollectionUtil.isEmpty(labels) && labels.get(0) != null) {
            groupService.sendGroupMessages(labels.get(0).getOrgId());
        }

        return ServiceResult.success(labelDTOS);
    }

    /**
     * label 转化 labelDto
     *
     * @param label source
     * @return LabelDTO
     */
    private LabelDTO convertToLabelDTO(LabelDO label) {
        LabelDTO labelDTO = new LabelDTO();

        if (label != null) {
            labelDTO.setId(label.getLabelId());
        }
        return labelDTO;
    }

    /**
     * 删除联系人标签以及标签值
     *
     * @param attribute     联系人类型
     * @param orgId         组织id
     * @param labelValueIds 需删除的标签值id列表
     */
    private boolean deleteLabelValue(Integer attribute, String orgId, List<String> labelValueIds) {
        // grpc 删除联系人相关的标签值
//        if (!xmMemberClient.deleteMemberLabels(attribute, orgId, labelValueIds)) {
//            log.error("联系人标签值删除失败，orgId {}, 标签值id {}", orgId, labelValueIds);
//            return false;
//        }

        // 根据标签值id列表 批量删除标签值
        Boolean deleteResult = labelValueService.deleteByIds(labelValueIds);
        if (!deleteResult) {
            log.error("标签值删除失败，orgId {}, 标签值id {}", orgId, labelValueIds);
            return false;
        }
        return true;
    }

    @Override
    public ServiceResult<List<LabelInfoDTO>> listLabels(ListLabelsParam param) {
        // 查询标签组下所有的标签信息
        List<LabelInfoDTO> labelInfoDTOS = labelGroupService.listLabels(param);
        return ServiceResult.success(labelInfoDTOS);
    }

    @Override
    public ServiceResult<Boolean> checkLabelName(CheckLabelNameParam param) {
        // 根据标签名称与组织id获取数据
        Integer count = labelDao.countByNameAndOrgId(param.getName(), param.getOrgId());
        // 判断是否存在
        boolean valid = count == 0 ? true : false;
        return ServiceResult.success(valid);
    }

    @Override
    public List<LabelDO> findByAttributeAndOrgId(String orgId) {
        return labelDao.listByOrgId(orgId);
    }
}
